2 * TETRIS for AVR microcontrollers and cascading LED matrix based on MAX7219 driver
3 * Copyright (C) 2010 Dmitry Shalnov [interplaymedium.org]
4 * test it with "screen /dev/ttyUSB0" comand (control by kbd arrows)
5 * created for Interplaymediumâ„¢ AXON project
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <avr/interrupt.h>
26 // please change it according to your ARDUINO wiring
27 // don't forget to place pull-up resisor between RESET and +5V pins to avoid board reset during UART transfer.
34 #define INUSE 1 // how many matrix you connect
35 #define STARTX 4 // horisontal position od figure strat
38 // Some macros that make the code more readable
40 #define output_low(port,pin) port &= ~(1<<pin)
41 #define output_high(port,pin) port |= (1<<pin)
42 #define set_input(portdir,pin) portdir &= ~(1<<pin)
43 #define set_output(portdir,pin) portdir |= (1<<pin)
47 #define UBRR FOSC/16/BAUD-1
49 volatile unsigned char u, oldu;
51 // ------------------------------------------- Set Receive Interrupt Enable -------------------------------------------
53 void setRXCIE_USART0()
55 UCSR0B |= _BV(RXCIE0);
58 // -------------------------------------------- Initialize USART0 -----------------------------------------------------
60 void init_USART0 (unsigned int baud) {
61 UBRR0 = baud; // Set Baudrate
62 UCSR0C = (3<<UCSZ00); // Character Size 8 bit
63 UCSR0B |= _BV(RXEN0) | _BV(TXEN0); // Receiver and Transmitter Enable
66 // -------------------------------------------- Receive 1 byte Data --------------------------------------------------
68 unsigned char receive_1byte_USART0 (void) {
69 loop_until_bit_is_set(UCSR0A, RXC0);
73 // -------------------------------------------- Transmit 1 byte Data --------------------------------------------------
75 void transmit_1byte_USART0 (unsigned char data) {
76 loop_until_bit_is_set(UCSR0A, UDRE0);
80 // -------------------------------------------- Interrupt catch -------------------------------------------------------
82 ISR(USART_RX_vect){ // USART RX interrupt
83 volatile unsigned char c;
85 //transmit_1byte_USART0('w');
86 //output_high(PORTB, LED1);
91 // --------------------------------------------- Transmit String Data -------------------------------------------------
93 void transmit_str_USART0 (char *str) {
95 transmit_1byte_USART0(*str);
100 // --------------------------------------------- Transmit Four-Digit Integer ------------------------------------------
102 void transmit_4digit_USART0 (int num) {
108 transmit_1byte_USART0('0'+temp);
114 // --------------------------------------------- Delays --------------------------------------------------------------
116 void delay_ms (uint16_t ms) {
117 uint16_t delay_count = FOSC / 17500 * 0.1;
120 for (i=0; i != delay_count; i++);
125 void delay_ns(uint16_t ms) {
126 uint16_t delay_count = FOSC / 17500 * 0.01;
129 for (i=0; i != delay_count; i++);
134 // --------------------------------------------- Sounds -------------------------------------------------------------
137 for(uint8_t a=0; a<100; a++){
138 output_high(PORTB, BEEPER);
140 output_low(PORTB, BEEPER);
145 void bleepBleepSound (){
147 for(b=1; b<40; b+=10) for(a=0; a<100; a++){
148 output_high(PORTB, BEEPER);
150 output_low(PORTB, BEEPER);
155 // --------------------------------- LED MATRIX code ---------------------------------------------------------------
157 // define max7219 registers
158 uint8_t max7219_reg_noop = 0x00;
159 uint8_t max7219_reg_digit0 = 0x01;
160 uint8_t max7219_reg_digit1 = 0x02;
161 uint8_t max7219_reg_digit2 = 0x03;
162 uint8_t max7219_reg_digit3 = 0x04;
163 uint8_t max7219_reg_digit4 = 0x05;
164 uint8_t max7219_reg_digit5 = 0x06;
165 uint8_t max7219_reg_digit6 = 0x07;
166 uint8_t max7219_reg_digit7 = 0x08;
167 uint8_t max7219_reg_decodeMode = 0x09;
168 uint8_t max7219_reg_intensity = 0x0a;
169 uint8_t max7219_reg_scanLimit = 0x0b;
170 uint8_t max7219_reg_shutdown = 0x0c;
171 uint8_t max7219_reg_displayTest = 0x0f;
174 void putByte(uint8_t data) {
178 mask = 0x01 << (i - 1); // get bitmask
179 //digitalWrite( CLOCK, LOW); // tick
180 output_low(PORTB, CLOCK); // tick
181 if (data & mask){ // choose bit
182 //digitalWrite(DATAIN, HIGH);// send 1
183 output_high(PORTB, DATAIN);
185 //digitalWrite(DATAIN, LOW); // send 0
186 output_low(PORTB, DATAIN);
188 //digitalWrite(CLOCK, HIGH); // tock
189 output_high(PORTB, CLOCK);
190 --i; // move to lesser bit
194 void maxOne(uint8_t maxNr, uint8_t reg, uint8_t col) {
195 //maxOne is for adressing different MAX7219's,
196 //whilele having a couple of them cascaded
198 //digitalWrite(load, LOW); // begin
199 output_low(PORTB, LOAD);
201 for ( c = INUSE; c > maxNr; c--) {
202 putByte(0); // means no operation
203 putByte(0); // means no operation
206 putByte(reg); // specify register
207 putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
209 for ( c = maxNr-1; c >= 1; c--) {
210 putByte(0); // means no operation
211 putByte(0); // means no operation
214 //digitalWrite(load, LOW); // and load da shit
215 output_low(PORTB, LOAD);
216 //digitalWrite(load,HIGH);
217 output_high(PORTB, LOAD);
221 void putPixel (uint8_t maxNr, uint8_t x, uint8_t y){
222 //maxOne is for adressing different MAX7219's,
223 //whilele having a couple of them cascaded
226 //digitalWrite(load, LOW); // begin
227 output_low(PORTB, LOAD);
229 for ( c = INUSE; c > maxNr; c--) {
230 putByte(0); // means no operation
231 putByte(0); // means no operation
234 putByte(y); // specify register
235 putByte(0x01 << (8-x));//((data & 0x01) * 256) + data >> 1); // put data
237 for ( c = maxNr-1; c >= 1; c--) {
238 putByte(0); // means no operation
239 putByte(0); // means no operation
242 //digitalWrite(load, LOW); // and load da shit
243 output_low(PORTB, LOAD);
244 //digitalWrite(load,HIGH);
245 output_high(PORTB, LOAD);
249 // --------------------------------- Figures ----------------------------------------------------------------------
251 uint8_t figure[5][4][4] = {
385 // ----------------------------------------- Init variables ------------------------------------------------------------
387 uint8_t screen[ INUSE*8 + 1 ];
389 uint8_t currentFigure = 4;
390 uint8_t currentTurn = 0;
391 uint8_t currentY = 0;
392 uint8_t currentX = STARTX;
394 uint8_t randomDigit = 0;
395 uint8_t prewRandomDigit;
398 // ------------------------------------------ Game logic ---------------------------------------------------------------
400 uint8_t moveLine(uint8_t L, uint8_t X){
401 if (X <= 4 ) L <<= 4 - X; else L >>= X - 4;
405 void redrawScreen (void) {
406 uint8_t lineToPlace = 0b00000000;
407 for (uint8_t matrix=0; matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
408 if (matrix*8 + line >= currentY && matrix*8 + line < currentY+4) lineToPlace = figure[currentFigure][currentTurn][matrix*8 +line - currentY]; else lineToPlace = 0b00000000;
409 //lineToPlace = 0b11111111; else lineToPlace = 0b00000000;
410 //if (currentX <= 4 ) lineToPlace <<= 4 - currentX; else lineToPlace >>= currentX - 4;
411 lineToPlace = moveLine(lineToPlace, currentX);
412 maxOne(matrix+1, line+1, screen[matrix*8 + line] | lineToPlace );
416 uint8_t checkDown (void){
417 uint8_t lineToPlace = 0b00000000;
418 for (uint8_t line = 0; line<4; line ++){
419 lineToPlace = figure[currentFigure][currentTurn][line];
420 lineToPlace = moveLine(lineToPlace, currentX);
421 if ( (lineToPlace & screen[ currentY+1 + line]) != 0) return 1;
426 uint8_t checkStart (void){
427 uint8_t lineToPlace = 0b00000000;
428 for (uint8_t line = 0; line<4; line ++){
429 lineToPlace = figure[currentFigure][currentTurn][line];
430 lineToPlace = moveLine(lineToPlace, currentX);
431 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
436 uint8_t checkLeft (void){
437 uint8_t lineToPlace = 0b00000000;
438 uint8_t tmpLineToPlace;
439 for (uint8_t line = 0; line<4; line ++){
440 lineToPlace = figure[currentFigure][currentTurn][line];
441 tmpLineToPlace = moveLine(lineToPlace, currentX);
442 if ( (tmpLineToPlace & 0b10000000) != 0) return 1;
443 lineToPlace = moveLine(lineToPlace, currentX - 1);
444 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
449 uint8_t checkRight (void){
450 uint8_t lineToPlace = 0b00000000;
451 uint8_t tmpLineToPlace;
452 for (uint8_t line = 0; line<4; line ++){
453 lineToPlace = figure[currentFigure][currentTurn][line];
454 tmpLineToPlace = moveLine(lineToPlace, currentX);
455 if ( (tmpLineToPlace & 0b00000001) != 0) return 1;
456 lineToPlace = moveLine(lineToPlace, currentX + 1);
457 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
462 uint8_t checkTurn (void){
463 uint8_t lineToPlace = 0b00000000;
465 if (currentTurn < 3) testTurn = currentTurn + 1; else testTurn = 0;
466 for (uint8_t line = 0; line<4; line ++){
467 lineToPlace = figure[currentFigure][testTurn][line];
468 lineToPlace = moveLine(lineToPlace, currentX);
469 //if ( (lineToPlace & 0b00000001) != 0) return 1;
470 if (currentFigure == 1 && currentX == 8) return 1;
471 if (currentFigure == 2 && currentX == 1) return 1;
472 if (currentFigure == 2 && currentX == 8) return 1;
473 if (currentFigure == 3 && currentX == 1) return 1;
474 if (currentFigure == 3 && currentX == 8) return 1;
475 if (currentFigure == 4 && currentX == 2) return 1;
476 if (currentFigure == 4 && currentX == 7) return 1;
477 if ( (lineToPlace & screen[ currentY + line]) != 0) return 1;
482 void checkFullLine (void) {
484 uint8_t matrix = currentY / (INUSE*8);
485 uint8_t remainder = currentY % (INUSE*8);
486 for (uint8_t line = 0; line<4; line ++) if (line + currentY < INUSE*8) {
487 if (screen[line + currentY] == 0b11111111) {
488 for (a = 0; a<3; a++) {
490 maxOne(matrix + 1, line + remainder +1, 0b00000000 );
491 //screen[line + currentY] = 0b11111111;
494 maxOne(matrix + 1, line + remainder +1, 0b11111111 );
495 //screen[line + currentY] = 0b00000000;
499 for (a = line + currentY; a>0; a--) screen[a] = screen[a-1];
501 transmit_str_USART0("Score: ");
502 transmit_4digit_USART0(score++);
503 transmit_str_USART0(" \r");
509 uint8_t ornament[3] = {0b00100100, 0b10010010, 0b01001001};
511 for (uint8_t matrix=0; matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
512 maxOne(matrix + 1, line + 1, 0b11111111 );
515 for (uint8_t matrix=0; matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
516 maxOne(matrix + 1, line + 1, 0b00000000 );
517 screen[ matrix*8 + line] = 0b00000000;
522 for (uint8_t matrix=0; matrix < INUSE; matrix++) for (uint8_t line=0; line<8; line++){
523 maxOne(matrix + 1, line + 1, ornament[(line+offset) % 3] );
525 if (offset < 2) offset ++; else offset = 0;
530 transmit_str_USART0("Score: 0000 \r");
532 currentFigure = (uint8_t)((uint16_t)timer % (uint16_t)5);
537 void stopFigure (void) {
538 uint8_t lineToPlace = 0b00000000;
539 for (uint8_t line = 0; line<4; line ++) {
540 lineToPlace = figure[currentFigure][currentTurn][line];
541 lineToPlace = moveLine(lineToPlace, currentX);
542 screen[line + currentY] |= lineToPlace;
548 currentFigure = randomDigit;
550 if (checkStart () != 1) redrawScreen (); else gameOver();
553 // --------------------------------- MAIN -----------------------------------------------------------------------
557 // initialize the direction of PORTD to be output
559 set_output(DDRB, DATAIN);
560 set_output(DDRB, LOAD);
561 set_output(DDRB, CLOCK);
562 set_output(DDRB, BEEPER);
564 // initiation of the max 7219
565 for (uint8_t matrix=0; matrix < INUSE; matrix++) {
566 maxOne(matrix+1, max7219_reg_scanLimit, 0x07);
567 maxOne(matrix+1, max7219_reg_decodeMode, 0x00); // using a led matrix, not digits
568 maxOne(matrix+1, max7219_reg_shutdown, 0x01); // not in shutdown mode
569 maxOne(matrix+1, max7219_reg_displayTest, 0x00); // no display test
570 for (uint8_t line=0; line<8; line ++) maxOne(matrix+1, line+1, 0); // empty registers, turn all LEDs off
571 maxOne(matrix+1, max7219_reg_intensity, 0x0f & 0x0f); // the first 0x0f is the value you can set (range: 0x00 to 0x0f)
574 init_USART0(UBRR); // initialize USART0
578 screen[ INUSE*8 ] = 0b11111111; // bottom border for checking
579 currentFigure = (uint8_t)((uint16_t)timer % (uint16_t)RAND_MAX);
580 transmit_str_USART0("Score: 0000 \r");
583 if (timer % 20000 == 0) {
585 if (checkDown() != 1) currentY ++; else {
586 if (prewRandomDigit == randomDigit) randomDigit++;
587 if (randomDigit > RAND_MAX+1) randomDigit = 0;
588 prewRandomDigit = randomDigit;
595 randomDigit = (uint8_t)((uint16_t)timer % (uint16_t)RAND_MAX);
596 //transmit_1byte_USART0(timer);
597 //transmit_4digit_USART0(u);
599 //transmit_str_USART0(" RIGHT \r");
600 if (checkRight () != 1) currentX ++;
605 //transmit_str_USART0(" LEFT \r");
606 if (checkLeft () != 1) currentX --;
611 //transmit_str_USART0(" DOWN \r");
612 if (checkDown() != 1) currentY ++;
617 //transmit_str_USART0(" UP \r");
618 if (checkTurn () != 1) {
619 if (currentTurn < 3) currentTurn ++; else currentTurn = 0;